﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using Metriks.Parsiranje.Klase;
using Metriks.Prezentacija.McCabe;
using Metriks.Parsiranje;
using Metriks.Metrike.MetrikaLOC;
using Metriks.Metrike;
using Metriks.Metrike.McCabe;
using Metriks.Refactoring;
using Metriks.Prezentacija;
using System.Text.RegularExpressions;
using Metriks.NezavisniPutevi;

namespace Metriks
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            // NOTE: haxx- poboljšavamo performanse richtextboxa za wpf :)
            System.Diagnostics.Process.GetCurrentProcess().ProcessorAffinity = (System.IntPtr)1; 

            InitializeComponent();
            syntaxHighlightEnabled = false;
        }

        bool syntaxHighlightEnabled { get; set; }

        private void loc_click(object sender, RoutedEventArgs e)
        {
            try
            {
                List<LOC> linesOfCode = getLOCS();
                LOCMetrika locm = new LOCMetrika(linesOfCode);
                MessageBox.Show(locm.assess());
                
            }
            catch (Exception ex)
            {
                MessageBox.Show("Linija: '"+ex.Message+"' nije prepoznata kao validna?");
            }
        }

        private void mccabe_num_click(object sender, RoutedEventArgs e)
        {
            try
            {
                McCabeMetrika mcm = new McCabeMetrika(getLOCS());
                MessageBox.Show(mcm.assess());
            }
            catch (Exception ex)
            {
                MessageBox.Show("Linija: '" + ex.Message + "' nije prepoznata kao validna?");
            }
        }

        private void mccabe_click(object sender, RoutedEventArgs e)
        {
            try
            {
                Cvor c = Struktura.pocetniCvor(getLOCS());
                GraphDisplay gd = new GraphDisplay(c);
                gd.ShowDialog();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Linija: '" + ex.Message + "' nije prepoznata kao validna?");
            }

        }

        /// <summary>
        /// Urađeno po naputcima predmetnog asistenta! :)
        /// TODO: prepoznavanje deklaracije varijable (još možda 30 min posla)
        /// </summary>
        private void rename_novi(object sender, RoutedEventArgs e)
        {
            String selektiraniTekst = Editor.Selection.Text;
            // prva linija obrane
            if (Editor.Selection.IsEmpty || !isAVariable(selektiraniTekst))
                return;

            if (SyntaxProvider.IsKnownTag(selektiraniTekst))
            {
                MessageBox.Show("Ne možete preimenovati ključnu riječ!");
                return;
            }

            Paragraph linijaUKojojJeRename = Editor.Selection.Start.Paragraph;

            int locnum = 0;
            foreach (Paragraph p in Editor.Document.Blocks)
            {
                if (p == linijaUKojojJeRename)
                    break;
                else
                    locnum++;
            }

            try
            {
                List<LOC> linesOfCode = getLOCS();

                int gornjagranica = findGornjaGranica(linesOfCode, locnum, selektiraniTekst);
                int donjagranica = findDonjaGranica(linesOfCode, gornjagranica);

                if (gornjagranica == 0)
                    donjagranica = linesOfCode.Count - 1;

                RenameWindow r = new RenameWindow(selektiraniTekst);
                r.ShowDialog();

                if (r.Status == true && (isEmptyRegex.IsMatch(r.NovoIme) || SyntaxProvider.IsKnownTag(r.NovoIme)))
                {
                    MessageBox.Show("Ne može se uraditi rename na blanko ili u keyword :-)");
                    return;
                }
                else if (r.Status == true && isAVariable(r.NovoIme))
                {
                    List<LOC> locs = linesOfCode.GetRange(gornjagranica, donjagranica-gornjagranica+1);
                    Renamer.renameVariable(locs, selektiraniTekst, r.NovoIme);
                    locs_to_tbox(linesOfCode);
                }
           }
           catch (Exception ex)
           {
               MessageBox.Show("Linija: '" + ex.Message + "' nije prepoznata kao validna?");
           }
        }

        // OVO NIJE BILO U POTPUNOSTI ISPRAVNO URADJENO U ZADACI
        private static int findGornjaGranica(List<LOC> linesOfCode, int locnum, String varname)
        {
            for (int i = locnum; i >= 0; i--)
            {
                varDecl vardcl = linesOfCode[i] as varDecl;
                varInit varinitial = linesOfCode[i] as varInit;
                if (vardcl != null && vardcl.varijabla.Naziv == varname)
                {
                    return i;
                }
                else if (varinitial != null && varinitial.varijabla.Naziv== varname)
                {
                    return i;
                }
            }
            return 0;
        }

        // OVO NIJE BILO U POTPUNOSTI ISPRAVNO URADJENO U ZADACI
        private static int findDonjaGranica(List<LOC> linesOfCode, int locnum)
        {
            Stack<int> stek = new Stack<int>();
            for (int i = locnum+1; i < linesOfCode.Count; i++)
            {
                bool jeKraj=linesOfCode[i] is beginElse || linesOfCode[i] is endIf || linesOfCode[i] is endWhile || linesOfCode[i] is endFunction;
                if (linesOfCode[i] is beginIf)
                {
                    stek.Push(1);
                    stek.Push(1);
                }
                else if (linesOfCode[i] is beginWhile)
                {
                    stek.Push(1);
                }
                else if (jeKraj && stek.Count!=0)
                {
                    stek.Pop();
                }
                else if (jeKraj && stek.Count == 0)
                {
                    return i;
                }
            }
            return linesOfCode.Count - 1;
        }

        private static Regex drugaLinijaObrane = new Regex(@"^[A-Za-z_][A-Za-z0-9_]*$");
        private static Regex isEmptyRegex = new Regex(@"^\s*$");


        private bool isAVariable(String selectedText)
        {
            return drugaLinijaObrane.IsMatch(selectedText);
        }

        private void rename_click(object sender, RoutedEventArgs e)
        {
            try
            {
                List<LOC> linijeKoda = getLOCS();
                RenameWindow r = new RenameWindow();
                r.ShowDialog();
                Regex isEmptyRegex= new Regex(@"^\s*$");
                if (r.Status==true && (isEmptyRegex.IsMatch(r.StaroIme) || isEmptyRegex.IsMatch(r.NovoIme)))
                {
                    MessageBox.Show("Ne može se raditi rename sa praznim imenima varijabli!");
                    return;
                }
                else if (r.Status==true && isAVariable(r.NovoIme) && !SyntaxProvider.IsKnownTag(r.NovoIme))
                {
                    Renamer.renameVariable(linijeKoda, r.StaroIme, r.NovoIme);
                    locs_to_tbox(linijeKoda);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Linija: '" + ex.Message + "' nije prepoznata kao validna?");
            }
        }

        private void extract_click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Nije implementirano jer nismo stigli :(");
//             try
//             {
//                 List<LOC> linijeKoda = getLOCS();
// 
//                 MessageBox.Show("Kaboom!");
// 
//                 linijeKoda = Iskazni.iskazniNivo(linijeKoda);
// 
//                 locs_to_tbox(linijeKoda);
//             }
//             catch (Exception)
//             {
//                 MessageBox.Show("Neka linija nije ispravno napisana!");
//             }
        }

        private void syntaxenable(object sender, RoutedEventArgs e)
        {
            syntaxHighlightEnabled = true;
            (Editor.Document.Blocks.LastBlock as Paragraph).Inlines.Add(new Run(""));
        }

        private void syntaxdisable(object sender, RoutedEventArgs e)
        {
            syntaxHighlightEnabled = false;
            TextPointer početak = Editor.Document.ContentStart;
            TextPointer kraj = Editor.Document.ContentEnd;
            TextRange tr = new TextRange(početak, kraj);
            tr.ClearAllProperties();
            
        }

        private void locs_to_tbox(List<LOC> linesOfCode)
        {
            Editor.Document.Blocks.Clear();
            foreach (LOC loc in linesOfCode)
            {
                Editor.Document.Blocks.Add(new Paragraph(new Run(loc.ispiši())));
            }
        }

        #region extraction_of_locs

        public List<LOC> getLOCS()
        {
            List<String> rawtext = getLines();
            return Parser.parsiraj(rawtext);
        }

        public List<String> getLines()
        {
            List<String> lines = new List<String>();
            foreach (Paragraph p in Editor.Document.Blocks)
            {
                lines.Add(new TextRange(p.ContentStart, p.ContentEnd).Text);
            }
            return lines;
        }

        #endregion

        #region generation_of_nodes

        // TODO @ hidex

        #endregion

        #region syntax_highlighter
        /*
         * Preuzeto i prilagođeno sa http://blogs.microsoft.co.il/blogs/tamir/archive/2006/12/14/RichTextBox-syntax-highlighting.aspx
         * 
         * */
        private void richTextBox1_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (Editor.Document == null || syntaxHighlightEnabled==false)
                return;

            TextRange documentRange = new TextRange(Editor.Document.ContentStart, Editor.Document.ContentEnd);
            documentRange.ClearAllProperties();

            TextPointer navigator = Editor.Document.ContentStart;
            while (navigator.CompareTo(Editor.Document.ContentEnd) < 0)
            {
                TextPointerContext context = navigator.GetPointerContext(LogicalDirection.Backward);
                if (context == TextPointerContext.ElementStart && navigator.Parent is Run)
                {
                    CheckWordsInRun((Run)navigator.Parent);
                }
                navigator = navigator.GetNextContextPosition(LogicalDirection.Forward);
            }

            Format();
        }

        new struct Tag
        {
            public TextPointer StartPosition;
            public TextPointer EndPosition;
            public string Word;

        }
        List<Tag> m_tags = new List<Tag>();
        private void Format()
        {
            Editor.TextChanged -= this.richTextBox1_TextChanged;

            for (int i = 0; i < m_tags.Count; i++)
            {
                TextRange range = new TextRange(m_tags[i].StartPosition, m_tags[i].EndPosition);
                range.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
            }
            m_tags.Clear();

            Editor.TextChanged += this.richTextBox1_TextChanged;
        }

        private void CheckWordsInRun(Run run)
        {
            string text = run.Text;

            int sIndex = 0;
            int eIndex = 0;
            for (int i = 0; i < text.Length; i++)
            {
                if (Char.IsWhiteSpace(text[i]) | SyntaxProvider.GetSpecials.Contains(text[i]))
                {
                    if (i > 0 && !(Char.IsWhiteSpace(text[i - 1]) | SyntaxProvider.GetSpecials.Contains(text[i - 1])))
                    {
                        eIndex = i - 1;
                        string word = text.Substring(sIndex, eIndex - sIndex + 1);

                        if (SyntaxProvider.IsKnownTag(word))
                        {
                            Tag t = new Tag();
                            t.StartPosition = run.ContentStart.GetPositionAtOffset(sIndex);
                            t.EndPosition = run.ContentStart.GetPositionAtOffset(sIndex+word.Length);
                            t.Word = word;
                            m_tags.Add(t);
                        }
                    }
                    sIndex = i + 1;
                }
            }

            string lastWord = text.Substring(sIndex, text.Length - sIndex);
            if (SyntaxProvider.IsKnownTag(lastWord))
            {
                Tag t = new Tag();
                t.StartPosition = run.ContentStart.GetPositionAtOffset(sIndex);
                t.EndPosition = run.ContentStart.GetPositionAtOffset(sIndex+lastWord.Length);
                t.Word = lastWord;
                m_tags.Add(t);
            }
        }

        #endregion


        #region zadaca4

        private void nezavisniputevigrafički(object sender, RoutedEventArgs e)
        {
            try
            {
                List<LOC> linesOfCode = getLOCS();
                Cvor c = Struktura.pocetniCvor(linesOfCode);
                McCabeMetrika mcm = new McCabeMetrika(linesOfCode);
                GraphDisplay gd = new GraphDisplay(c, mcm.getKompleksnost());
                gd.ShowDialog();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Linija: '" + ex.Message + "' nije prepoznata kao validna ili je ovo neki generalni izuetak :( Nadam se da je ovo prvo :D");
            }
        }

        #endregion

    }
}
